;; EX-04.scm - Dodge and Jerse Examples 

;; This file includes expressions to implement examples from the
;; second edition of the book:

;;      Charles Dodge and Thomas Jerse. 
;;      "Computer Music: Synthesis, Composition, and Performance."
;;      Schirmer, New York, 1997.

;; The expressions are written to most clearly reflect the signal-flow
;; drawings as given in the text and do not make any language specific
;; optimizations or rely upon any `syntactic rewriting'.

;; The expressions are not written as instruments to be instantiated
;; with arguments but as singular expression to construct the
;; indicated UGen graph when evaluated.

;; Variable names are identical to those in the text, including case.
;; This is for clarity when comparing the expression to the signal
;; flow drawing only.

;; The figure and page numbers given in parentheses refer to the first
;; edition of the book.  Not all examples appear in both editions.

;; FIGURE 4.5, pg. 81 (FIGURE 3.5, pg. 71)

;; The SC3 EnvGen UGen is rather sophisticated, and includes a
;; real-time trigger for release and sustain sections.  This helper
;; procedure makes a basic Attack-Sustain-Release envelope of fixed
;; duration.

(define (BasicASR rise-time amp decay-time dur)
  (EnvGen.kr envelope: (Env (list 0 amp amp 0)
			    (list rise-time 
				  (- dur rise-time decay-time)
				  decay-time))
	     doneAction: 2))

(let ((RISE-TIME 0.05)
      (AMP 0.1)
      (DUR 2.0)
      (DECAY-TIME 0.7)
      (FREQ 220.0))
  (SinOsc.ar mul: (BasicASR RISE-TIME AMP DECAY-TIME DUR)
	     freq: FREQ))

;; EXAMPLE 4.1, pg. 87 and FIGURE 4.11a, pg. 86 (EXAMPLE 3.1, pg. 76)

;; instr 1
;; k1 linen p5,p6,p3,p8
;; a2 oscil k1,p4,2
;; out a2
;; endin

;; In CSound and MUSIC 11 `p1' gives the instrument number, `p2' the
;; start time for the note, and `p3' the duration of the note.

(begin
  (define (instr1 p3 p4 p5 p6 p8 f2)
    (Osc.ar (bId f2) 
	    freq: p4
	    mul: (BasicASR p6 p5 p8 p3)))
  (define f2 (buffer-alloc 8192 1 #t))
  (buffer-sine1 f2 '(1) #f #t #t))

(instr1 2.4 440.0 0.1 0.5 0.8 f2)

(buffer-free f2)

;; FIGURE 4.13, pg. 91 (FIGURE 3.13, pg. 81)

(let ((m 1.0)
      (AMP 0.1)
      (fm 42.0)
      (fc 440.0))
  (SinOsc.ar mul: (Add (SinOsc.ar mul: (Mul m AMP)
				  freq: fm)
		       AMP)
	     freq: fc))

;; FIGURE 4.15, pg. 92 (FIGURE 3.15, pg. 82)

(let ((A 0.1)
      (fm 440.0)
      (fc 260.0))
  (SinOsc.ar mul: (SinOsc.ar mul: A freq: fm)
	     freq: fc))

;; FIGURE 4.17, pg. 93 (FIGURE 3.17, pg. 83)

;; The figure does not specify either input, here each is a sinusoidal
;; oscillator.

(let ((INPUT-1 (SinOsc.ar 550 0 0.1))
      (INPUT-2 (SinOsc.ar 660 0.1)))
  (Mul INPUT-1 INPUT-2))

;; FIGURE 4.19, pg. 95 (FIGURE 3.20, pg. 86)

(let* ((fc 440.0)
       (VIB-WIDTH (* fc 0.02))
       (VIB-RATE 4.3)
       (AMP 0.1))
  (SinOsc.ar mul: AMP
	     freq: (Add fc (SinOsc.ar mul: VIB-WIDTH
				      freq: VIB-RATE))))

;; FIGURE 4.24, pg. 100

;; NOT IMPLEMENTED

;; FIGURE 4.26, pg. 103 (FIGURE 3.25, pg. 92)

;; The figure is missing the DUR variable at the envelope generator.

(let* ((RISE-TIME 0.6)
       (AMP 0.1)
       (DECAY-TIME 0.8)
       (DUR 8.0)
       (FREQ 400.0)
       (fR (* FREQ 0.2)))
  (SinOsc.ar mul: (LFNoise1.ar mul: (BasicASR RISE-TIME AMP DECAY-TIME DUR)
			       freq: fR)
	     freq: FREQ))

;; FIGURE 4.28, pg. 105 (FIGURE 3.27, pg. 94)

;; The AMP variable is divided by eleven, the number of oscillators,
;; to approriately scale the output.  Note that the done action is set
;; to free the synth only at the end of the note.

(let ((amplitude-data '(1.0 0.67 1.0 1.8 2.67 1.67 1.46 1.33 1.33 1.0 1.33))
      (duration-data '(1.0 0.9 0.65 0.55 0.325 0.35 0.25 0.2 0.15 0.1 0.075))
      (frequency-data '(0.56 0.56 0.92 0.92 1.19 1.7 2.0 2.74 3.0 3.76 4.07))
      (frequency-offset-data '(0 1 0 1.7 0 0 0 0 0 0 0))
      (AMP (* 0.5 (/ 1 11)))
      (DUR 20.0)
      (FREQ 360.0))
  (define (ENV a d)
    (EnvGen.kr envelope: (Env.perc releaseTime: (* DUR d)
				   level: (* AMP a))
	       gate: 1.0 
	       doneAction: (if (= d 1) 2 0)))
  (Mix (map (lambda (a d f* f+)
	      (SinOsc.ar mul: (ENV a d)
			 freq: (+ (* FREQ f*) f+)))
	    amplitude-data
	    duration-data
	    frequency-data
	    frequency-offset-data)))

;; FIGURE 5.1, pg. 116 (FIGURE 5.1, pg. 106)

(let ((fc 440)
      (d 100)
      (fm 380)
      (AMP 0.2))
  (SinOsc.ar mul: AMP
	     freq: (Add fc (SinOsc.ar mul: d
				      freq: fm))))

;; FIGURE 5.11, pg. 128 (FIGURE 4.11, pg. 117)

;; The expression amplitude input of the topmost oscillator is wrongly
;; written, it should read 'I1 * fm'.

(let ((fm 660)
      (fc1 330)
      (fc2 550)
      (I1 3)
      (I2 2)
      (AMP 0.3)
      (A2 0.2))
  (let* ((modulator (SinOsc.ar mul: (Mul I1 fm)
			       freq: fm))
	 (carrier-one (SinOsc.ar mul: AMP
				 freq: (Add modulator fc1)))
	 (carrier-two (SinOsc.ar mul: (Mul AMP A2)
				 freq: (Add (Mul modulator (FDiv I2 I1)) fc2))))
    (Add carrier-one carrier-two)))

;; FIGURE 5.34, pg. 162 (FIGURE 4.36, pg. 152)

;; Abstracted conditional ugen math.  All arguments are ugens.
;; Evaluates to a ugen that has the value of `true' when the signal
;; `predicate' is 1, and the value `false' when `predicate' is zero.
;; The expression here uses the identity '(+ (* predicate true) (* (-
;; 1 predicate) false))' == '(+ (* zero-or-one (- true false))
;; false)'.

(define (If predicate true false)
  (Add (Mul predicate (Sub true false)) false))

(let ((FREQ 440)
      (AMP 0.2)
      (N 10))
  (let ((a (SinOsc.ar mul: 1
		      freq: (/ (* FREQ (+ (* 2 N) 1)) 2)))
	(b (SinOsc.ar mul: 1
		      freq: (/ FREQ 2))))
    ;; This demonstrates conditional math at ugens.  The output is
    ;; given by the expression `near-zero' if the absolute value of
    ;; `b' is less than `epsilon', else the output is given by the
    ;; expression `not-near-zero'.
    (let ((epsilon 1.0e-9)
	  (near-zero AMP)
	  (not-near-zero (Mul (FDiv AMP (Mul 2 N))
			      (Add -1 (FDiv a b)))))
      (If (LT (Abs b) epsilon) near-zero not-near-zero))))

;; FIGURE 6.17, pg. 182 (FIGURE 5.16, pg. 167)

;; The figure does not specify INPUT, here it is a sinusoidal
;; oscillator.  

;; The output signal of this graph is not an audio signal, it is an
;; estimate of the average signal power of INPUT.

(let* ((CUTOFF-FREQ 10)
       (AMP 0.6)
       (INPUT (SinOsc.ar 440.0 0 AMP)))
  (LPF.ar (Abs INPUT) CUTOFF-FREQ))

;; FIGURE 6.27, pg. 192 (FIGURE 5.25, pg. 176)

;; NOT IMPLEMENTED

;; Figure 10.11.a, pg.299 (Figure 7.11.a, pg.233)

;; A Schroeder topology reverberator.  All times are given in seconds.
;; The reverberator is implemented as a procedure.  The comb and
;; all-pass filter parameters are those given in Table 10.1, pg. 301.

(define (SchroederReverb.ar in reverbTime gain)
  (AllpassN.ar 
   (AllpassN.ar
    (Mix (CombN.ar in 0.05 '(0.0297 0.0371 0.0411 0.0437) reverbTime 1/4))
    0.01 0.005 0.096835)
   0.01 0.0017 0.032924 gain))

(let ((input-signal (Dust.ar 1 0.5))
      (reverb-time 12)
      (mix 0.75))
  (Add (Mul input-signal (- 1 mix))
       (SchroederReverb.ar input-signal reverb-time mix)))

;; Figure 10.15, pg. 304 (Figure 7.15, pg. 237)

;; NOT IMPLEMENTED

;; Figure 10.16, pg.305

;; NOT IMPLEMENTED
